#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/poll.h>
#include <time.h>
#include <unistd.h>
#include <stdatomic.h>

#include "rk_debug.h"
#include "rk_defines.h"
#include "rk_mpi_adec.h"
#include "rk_mpi_aenc.h"
#include "rk_mpi_ai.h"
#include "rk_mpi_ao.h"
#include "rk_mpi_avs.h"
#include "rk_mpi_cal.h"
#include "rk_mpi_ivs.h"
#include "rk_mpi_mb.h"
#include "rk_mpi_rgn.h"
#include "rk_mpi_sys.h"
#include "rk_mpi_tde.h"
#include "rk_mpi_vdec.h"
#include "rk_mpi_venc.h"
#include "rk_mpi_vi.h"
#include "rk_mpi_vo.h"
#include "rk_mpi_vpss.h"

#include <rk_aiq_user_api2_camgroup.h>
#include <rk_aiq_user_api2_imgproc.h>
#include <rk_aiq_user_api2_sysctl.h>

#define Debug(...) ((void)0) // 将 printf 替换为空操作
#define DebugErr printf

#define MAX_AIQ_CTX 2 // 摄像头数量
#define SEC_FRAME_NUM 25 // 每秒帧数

static rk_aiq_sys_ctx_t *g_aiq_ctx[MAX_AIQ_CTX];
static rk_aiq_working_mode_t g_work_mode[MAX_AIQ_CTX];
static const char *g_iq_dir = "/oem/usr/share/iqfiles";
static const char *g_out_dir = "/tmp";
static const char *g_prefix = "out_";
static RK_S32 g_out_jpeg_num = 1;
static atomic_bool g_should_quit = false;

static void sigterm_handler(int sig)
{
	fprintf(stderr, "signal %d\n", sig);
	g_should_quit = true;
}

RK_U64 GetNowUs()
{
	struct timespec time = {0, 0};
	clock_gettime(CLOCK_MONOTONIC, &time);
	return (RK_U64)time.tv_sec * 1000000 + (RK_U64)time.tv_nsec / 1000; /* microseconds */
}

static void *GetMediaBuffer0(void *arg)
{
	(void)arg;
	Debug("========%s========\n", __func__);

	static RK_U32 jpeg_id = 0;
  static RK_U32 frameCnt = 0;
	char jpeg_path[64];

	VENC_STREAM_S stFrame;
	stFrame.pstPack = malloc(sizeof(VENC_PACK_S));

	while (!g_should_quit) {
		int s32Ret = RK_MPI_VENC_GetStream(0, &stFrame, 500); // 到达设置的照片数后会返回错误
		if (s32Ret != RK_SUCCESS) {
      DebugErr("Get stream[%d] ret: %d\n", frameCnt, s32Ret);
      g_should_quit = true;
      break;
    }

    // 保存照片
    if (frameCnt++ % SEC_FRAME_NUM == 0) {
      sprintf(jpeg_path, "%s/%s%d.jpeg", g_out_dir, g_prefix, jpeg_id+1);
      FILE *file = fopen(jpeg_path, "w");
      if (file) {
        void *pData = RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk);
        fwrite(pData, 1, stFrame.pstPack->u32Len, file);
        fflush(file);
        fclose(file);
      }
      jpeg_id++;
      Debug("--->>> chn:0, jpeg_id:%d enc->seq:%d wd:%d pts=%lld delay=%lldus\n",
              jpeg_id, stFrame.u32Seq, stFrame.pstPack->u32Len,
              stFrame.pstPack->u64PTS, GetNowUs() - stFrame.pstPack->u64PTS);
      printf("JPEG_OUT:%s\n", jpeg_path);
    }

    s32Ret = RK_MPI_VENC_ReleaseStream(0, &stFrame);
    if (s32Ret != RK_SUCCESS) {
      DebugErr("RK_MPI_VENC_ReleaseStream fail %x", s32Ret);
    }
	} // end while

	free(stFrame.pstPack);
	return NULL;
}

static RK_S32 test_venc_init(int chnId, int width, int height, int photoNum)
{
	Debug("========%s========\n", __func__);

	VENC_CHN_ATTR_S stAttr;
	memset(&stAttr, 0, sizeof(VENC_CHN_ATTR_S));
	stAttr.stVencAttr.enType = RK_VIDEO_ID_JPEG;
	stAttr.stVencAttr.enPixelFormat = RK_FMT_YUV422_UYVY;
	stAttr.stVencAttr.u32PicWidth = width;
	stAttr.stVencAttr.u32PicHeight = height;
	stAttr.stVencAttr.u32VirWidth = width;
	stAttr.stVencAttr.u32VirHeight = height;
	stAttr.stVencAttr.u32StreamBufCnt = 4;
	stAttr.stVencAttr.u32BufSize = width * height * 3 / 2;
	stAttr.stVencAttr.enMirror = MIRROR_NONE;

	stAttr.stVencAttr.stAttrJpege.bSupportDCF = RK_FALSE;
	stAttr.stVencAttr.stAttrJpege.stMPFCfg.u8LargeThumbNailNum = 0;
	stAttr.stVencAttr.stAttrJpege.enReceiveMode = VENC_PIC_RECEIVE_SINGLE;
	RK_MPI_VENC_CreateChn(chnId, &stAttr);

	VENC_CHN_PARAM_S stParam;
	memset(&stParam, 0, sizeof(VENC_CHN_PARAM_S));
	stParam.stFrameRate.bEnable = RK_FALSE;
	stParam.stFrameRate.s32SrcFrmRateNum = SEC_FRAME_NUM;
	stParam.stFrameRate.s32SrcFrmRateDen = 1;
	stParam.stFrameRate.s32DstFrmRateNum = SEC_FRAME_NUM;
	stParam.stFrameRate.s32DstFrmRateDen = 1;
	RK_MPI_VENC_SetChnParam(chnId, &stParam);

  // 选择角度
	RK_MPI_VENC_SetChnRotation(chnId, ROTATION_180);

  // jpeg参数设置
	VENC_JPEG_PARAM_S stJpegParam;
	memset(&stJpegParam, 0, sizeof(stJpegParam));
	stJpegParam.u32Qfactor = 99;
	RK_MPI_VENC_SetJpegParam(0, &stJpegParam);

  VENC_RECV_PIC_PARAM_S stRecvParam;
	memset(&stRecvParam, 0, sizeof(VENC_RECV_PIC_PARAM_S));
	stRecvParam.s32RecvPicNum = photoNum;
	RK_MPI_VENC_StartRecvFrame(chnId, &stRecvParam);
	return 0;
}

int vi_dev_init(int devId)
{
	Debug("%s\n", __func__);
	int pipeId = devId;

	VI_DEV_ATTR_S stDevAttr;
	VI_DEV_BIND_PIPE_S stBindPipe;
	memset(&stDevAttr, 0, sizeof(stDevAttr));
	memset(&stBindPipe, 0, sizeof(stBindPipe));

	// 0. get dev config status
	int ret = RK_MPI_VI_GetDevAttr(devId, &stDevAttr);
	if (ret == RK_ERR_VI_NOT_CONFIG) {
		// 0-1.config dev
		ret = RK_MPI_VI_SetDevAttr(devId, &stDevAttr);
		if (ret != RK_SUCCESS) {
			DebugErr("RK_MPI_VI_SetDevAttr %x\n", ret);
			return -1;
		}
	} else {
		Debug("RK_MPI_VI_SetDevAttr already\n");
	}
	// 1.get dev enable status
	ret = RK_MPI_VI_GetDevIsEnable(devId);
	if (ret != RK_SUCCESS) {
		// 1-2.enable dev
		ret = RK_MPI_VI_EnableDev(devId);
		if (ret != RK_SUCCESS) {
			DebugErr("RK_MPI_VI_EnableDev %x\n", ret);
			return -1;
		}
		// 1-3.bind dev/pipe
		stBindPipe.u32Num = 1;
		stBindPipe.PipeId[0] = pipeId;
		ret = RK_MPI_VI_SetDevBindPipe(devId, &stBindPipe);
		if (ret != RK_SUCCESS) {
			DebugErr("RK_MPI_VI_SetDevBindPipe %x\n", ret);
			return -1;
		}
	} else {
		Debug("RK_MPI_VI_EnableDev already\n");
	}

	return 0;
}

int vi_chn_init(int channelId, int width, int height)
{
	VI_CHN_ATTR_S vi_chn_attr;
	memset(&vi_chn_attr, 0, sizeof(vi_chn_attr));
	vi_chn_attr.stIspOpt.u32BufCount = 3;
	vi_chn_attr.stIspOpt.enMemoryType = VI_V4L2_MEMORY_TYPE_DMABUF; // VI_V4L2_MEMORY_TYPE_MMAP;
	vi_chn_attr.stSize.u32Width = width;
	vi_chn_attr.stSize.u32Height = height;
	vi_chn_attr.enPixelFormat = RK_FMT_YUV422_UYVY;
	vi_chn_attr.enCompressMode = COMPRESS_MODE_NONE; // COMPRESS_AFBC_16x16;
	vi_chn_attr.u32Depth = 0; //0, get fail, 1 - u32BufCount, can get, if bind to other device, must be < u32BufCount
	int ret = RK_MPI_VI_SetChnAttr(0, channelId, &vi_chn_attr);
	ret |= RK_MPI_VI_EnableChn(0, channelId);
	if (ret) {
		DebugErr("ERROR: create VI error! ret=%d\n", ret);
		return ret;
	}
	return ret;
}



// 设置isp参数
int rk_isp_set_contrast(rk_aiq_sys_ctx_t *aiq_ctx, int b, int c, int s)
{
	acp_attrib_t attrib;
	int ret = rk_aiq_user_api2_acp_GetAttrib(aiq_ctx, &attrib);
  attrib.brightness = b * 2.55; // value[0,255] 亮度
	attrib.contrast = c * 2.55; // value[0,255] 对比度
  attrib.saturation = s * 2.55; // value[0,255] 饱和度
	ret |= rk_aiq_user_api2_acp_SetAttrib(aiq_ctx, &attrib);
  if (ret) {
		DebugErr("ERROR: set cam error! ret = %d\n", ret);
  }
	return ret;
}

// 设置帧率
int rk_isp_set_frame_rate(rk_aiq_sys_ctx_t *aiq_ctx, int value)
{
	Uapi_ExpSwAttrV2_t expSwAttr;
	int ret = rk_aiq_user_api2_ae_getExpSwAttr(aiq_ctx, &expSwAttr);
	if (value == 0) {
		expSwAttr.stAuto.stFrmRate.isFpsFix = false;
	} else {
		expSwAttr.stAuto.stFrmRate.isFpsFix = true;
		expSwAttr.stAuto.stFrmRate.FpsValue = value;
	}
	ret = rk_aiq_user_api2_ae_setExpSwAttr(aiq_ctx, expSwAttr);
  if (ret) {
		DebugErr("ERROR: set frame rate error! ret = %d\n", ret);
  }
	return ret;
}

// 设置曝光时间，设置没用
int rk_isp_set_exposure_time(rk_aiq_sys_ctx_t *aiq_ctx, const char *value)
{
	Uapi_ExpSwAttrV2_t stExpSwAttr;
	float den, num, result;
	if (strchr(value, '/') == NULL) {
		den = 1;
		sscanf(value, "%f", &result);
	} else {
		sscanf(value, "%f/%f", &num, &den);
		result = num / den;
	}
	rk_aiq_user_api2_ae_getExpSwAttr(aiq_ctx, &stExpSwAttr);
	stExpSwAttr.stManual.LinearAE.TimeValue = result;
	stExpSwAttr.stManual.HdrAE.TimeValue[0] = result;
	stExpSwAttr.stManual.HdrAE.TimeValue[1] = result;
	stExpSwAttr.stManual.HdrAE.TimeValue[2] = result;
	int ret = rk_aiq_user_api2_ae_setExpSwAttr(aiq_ctx, stExpSwAttr);
  if (ret) {
		DebugErr("ERROR: set exp time error! ret = %d\n", ret);
  }
	return ret;
}

// 设置曝光增益，设置没用
int rk_isp_set_exposure_gain(rk_aiq_sys_ctx_t *aiq_ctx, int value)
{
	Uapi_ExpSwAttrV2_t stExpSwAttr;
	float gain_set = (value * 1.0f);
	rk_aiq_user_api2_ae_getExpSwAttr(aiq_ctx, &stExpSwAttr);
	if ((stExpSwAttr.stManual.LinearAE.ManualGainEn = false) ||
	    (stExpSwAttr.stManual.HdrAE.ManualGainEn = false)) {
		DebugErr("exposure mode is auto, not support set gain\n");
		return 0;
	}
	stExpSwAttr.stManual.LinearAE.GainValue = gain_set;
	stExpSwAttr.stManual.HdrAE.GainValue[0] = gain_set;
	stExpSwAttr.stManual.HdrAE.GainValue[1] = gain_set;
	stExpSwAttr.stManual.HdrAE.GainValue[2] = gain_set;
	int ret = rk_aiq_user_api2_ae_setExpSwAttr(aiq_ctx, stExpSwAttr);
  if (ret) {
		DebugErr("ERROR: set exp gain error! ret = %d\n", ret);
  }
	return ret;
}


static XCamReturn SIMPLE_COMM_ISP_SofCb(rk_aiq_metas_t *meta)
{
	//printf("$%u \n", meta->frame_id);
	return XCAM_RETURN_NO_ERROR;
}

static XCamReturn SIMPLE_COMM_ISP_ErrCb(rk_aiq_err_msg_t *msg)
{
  DebugErr("--- SIMPLE_COMM_ISP_ErrCb %d\n", msg->err_code);
	if (msg->err_code == XCAM_RETURN_BYPASS) g_should_quit = true;
	return XCAM_RETURN_NO_ERROR;
}

RK_S32 SIMPLE_COMM_ISP_Init(RK_S32 CamId, rk_aiq_working_mode_t WDRMode, RK_BOOL MultiCam, const char *iq_file_dir)
{
	if (CamId >= MAX_AIQ_CTX) {
		DebugErr("%s : CamId is over %d\n", __FUNCTION__, MAX_AIQ_CTX);
		return -1;
	}
	if (iq_file_dir == NULL) {
		DebugErr("SIMPLE_COMM_ISP_Init : not start.\n");
		g_aiq_ctx[CamId] = NULL;
		return 0;
	}

	// must set HDR_MODE, before init
	char hdr_str[16];
	snprintf(hdr_str, sizeof(hdr_str), "%d", (int)WDRMode);
	setenv("HDR_MODE", hdr_str, 1);
  g_work_mode[CamId] = WDRMode;

	rk_aiq_static_info_t aiq_static_info;
	rk_aiq_uapi2_sysctl_enumStaticMetas(CamId, &aiq_static_info);
	Debug("ID: %d, sensor_name is %s, iqfiles is %s\n", CamId, aiq_static_info.sensor_info.sensor_name, iq_file_dir);

  rk_aiq_sys_ctx_t *aiq_ctx;
	aiq_ctx = rk_aiq_uapi2_sysctl_init(aiq_static_info.sensor_info.sensor_name, iq_file_dir, SIMPLE_COMM_ISP_ErrCb, SIMPLE_COMM_ISP_SofCb);

  // 设置曝光度
  paRange_t range;
  range.min = 6.0f;
  range.max = 10.0f;
  rk_aiq_uapi2_setExpGainRange(aiq_ctx, &range);

	if (MultiCam) rk_aiq_uapi2_sysctl_setMulCamConc(aiq_ctx, true);
	g_aiq_ctx[CamId] = aiq_ctx;
	return 0;
}

RK_S32 SIMPLE_COMM_ISP_Run(RK_S32 CamId)
{
	if (CamId >= MAX_AIQ_CTX || !g_aiq_ctx[CamId]) {
		DebugErr("%s : CamId is over %d or not init\n", __FUNCTION__, MAX_AIQ_CTX);
		return -1;
	}
	if (rk_aiq_uapi2_sysctl_prepare(g_aiq_ctx[CamId], 0, 0, g_work_mode[CamId])) {
		DebugErr("rkaiq engine prepare failed !\n");
		g_aiq_ctx[CamId] = NULL;
		return -1;
	}
	Debug("rk_aiq_uapi2_sysctl_init/prepare succeed\n");

	if (rk_aiq_uapi2_sysctl_start(g_aiq_ctx[CamId])) {
		DebugErr("rk_aiq_uapi2_sysctl_start  failed\n");
		return -1;
	}
	Debug("rk_aiq_uapi2_sysctl_start succeed\n");

	return 0;
}

RK_S32 SIMPLE_COMM_ISP_Stop(RK_S32 CamId)
{
	if (CamId >= MAX_AIQ_CTX || !g_aiq_ctx[CamId]) {
		DebugErr("%s : CamId is over %d or not init g_aiq_ctx[%d] = %p\n", __FUNCTION__, MAX_AIQ_CTX, CamId, g_aiq_ctx[CamId]);
		return -1;
	}

	Debug("rk_aiq_uapi2_sysctl_stop enter\n");
	rk_aiq_uapi2_sysctl_stop(g_aiq_ctx[CamId], false);
	Debug("rk_aiq_uapi2_sysctl_deinit enter\n");
	rk_aiq_uapi2_sysctl_deinit(g_aiq_ctx[CamId]);
	Debug("rk_aiq_uapi2_sysctl_deinit exit\n");

	g_aiq_ctx[CamId] = NULL;
	return 0;
}

static RK_CHAR optstr[] = "?::w:h:o:c:n:";
static void print_usage(const RK_CHAR *name)
{
	printf("Usage example:\n");
	printf("\t%s -I 0 -w 1920 -h 1080 \n", name);
	printf("\t-w | --width: VI width, Default:1920\n");
	printf("\t-h | --heght: VI height, Default:1080\n");
	printf("\t-c | --jpeg count: jpeg number of output, Default:5\n");
  printf("\t-o | --out path: Default:\"/tmp\"\n");
  printf("\t-n | --jpeg name prefix: Default:\"out_\"\n");
}

int main(int argc, char *argv[])
{
	RK_S32 s32Ret = RK_FAILURE;
	RK_U32 u32Width = 1920;
	RK_U32 u32Height = 1080;
	RK_S32 s32chnlId = 0;

	int c;
	int ret = -1;

	while ((c = getopt(argc, argv, optstr)) != -1) {
		switch (c) {
		case 'w':
			u32Width = atoi(optarg);
			break;
		case 'h':
			u32Height = atoi(optarg);
			break;
    case 'o':
			if (optarg) g_out_dir = optarg;
			break;
		case 'c':
			g_out_jpeg_num = atoi(optarg);
      break;
    case 'n':
			if (optarg) g_prefix = optarg;
			break;
		case '?':
		default:
			print_usage(argv[0]);
			return -1;
		}
	}

	Debug("# Resolution: %dx%d\n", u32Width, u32Height);
	Debug("# CameraIdx: %d\n\n", s32chnlId);
	Debug("# Frame Count to save: %d\n", g_out_jpeg_num);
	signal(SIGINT, sigterm_handler);

  Debug("ISP IQ file path: %s\n\n", g_iq_dir);
  SIMPLE_COMM_ISP_Init(0, RK_AIQ_WORKING_MODE_NORMAL, false, g_iq_dir);
  SIMPLE_COMM_ISP_Run(0);

	if (RK_MPI_SYS_Init() != RK_SUCCESS) {
		RK_LOGE("rk mpi sys init fail!");
		goto __FAILED;
	}

	vi_dev_init(s32chnlId);
	vi_chn_init(s32chnlId, u32Width, u32Height);

	// venc init
	test_venc_init(0, u32Width, u32Height, (g_out_jpeg_num-1) * SEC_FRAME_NUM + 1);

	// bind vi to venc
  MPP_CHN_S stSrcChn, stDestChn;
	stSrcChn.enModId = RK_ID_VI;
	stSrcChn.s32DevId = 0;
	stSrcChn.s32ChnId = s32chnlId;
	stDestChn.enModId = RK_ID_VENC;
	stDestChn.s32DevId = 0;
	stDestChn.s32ChnId = 0;
	Debug("==== RK_MPI_SYS_Bind vi0 to venc0 ====\n");
	s32Ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn);
	if (s32Ret != RK_SUCCESS) {
		RK_LOGE("bind 0 ch venc failed");
		goto __FAILED;
	}

  pthread_t main_thread;
	pthread_create(&main_thread, NULL, GetMediaBuffer0, NULL);
	Debug("--->>> wait thread quit...\n");
	pthread_join(main_thread, NULL);
  sleep(1);
  Debug("--->>> now thread quit...\n");

  // 退出流程
	s32Ret = RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn);
	if (s32Ret != RK_SUCCESS) {
		DebugErr("RK_MPI_SYS_UnBind fail %x", s32Ret);
	}

	s32Ret = RK_MPI_VI_DisableChn(0, s32chnlId);
	Debug("RK_MPI_VI_DisableChn %x", s32Ret);

	s32Ret = RK_MPI_VENC_StopRecvFrame(0);
	if (s32Ret != RK_SUCCESS) {
		return s32Ret;
	}

	s32Ret = RK_MPI_VENC_DestroyChn(0);
	if (s32Ret != RK_SUCCESS) {
		DebugErr("RK_MPI_VDEC_DestroyChn fail %x", s32Ret);
	}

	s32Ret = RK_MPI_VI_DisableDev(0);
	Debug("RK_MPI_VI_DisableDev %x", s32Ret);
	ret = 0;

__FAILED:
  if (s32Ret)	DebugErr("work exit: %d\n", s32Ret);
	RK_MPI_SYS_Exit();

	SIMPLE_COMM_ISP_Stop(0);

	return ret;
}
